-
Notifications
You must be signed in to change notification settings - Fork 111
feat: some data management APIs #176
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
审阅者指南本次更新全面改进了 Fedy 集成,通过引入专用的数据同步和卡片生命周期端点,重构内部事件模型和通知流程,更新控制器和游戏处理程序以发出卡片事件,增强迁移/孤立逻辑与明确的游戏上下文,并移除旧的用户链接实体,从而采用基于卡片的数据管理。 移除 AquaNetUserFedy 实体和表的 ER 图erDiagram
AQUA_NET_USER_FEDY {
au_id BIGINT PK
created_at TIMESTAMP
}
AQUA_NET_USER_FEDY ||--|| AQUA_NET_USER : "au_id references au_id"
%% Table AQUA_NET_USER_FEDY is removed
新 Fedy 事件模型和端点的类图classDiagram
class Fedy {
+handleDataPull(key: Str, req: DataPullReq): DataPullRes
+handleDataPush(key: Str, req: DataPushReq): Any
+handleCardResolve(key: Str, req: CardResolveReq): CardResolveRes
+handleCardLink(key: Str, req: CardLinkReq): Any
+handleCardUnlink(key: Str, req: CardUnlinkReq): Any
+onCardCreated(luid: Str, extId: Long)
+onCardLinked(luid: Str, oldExtId: Long?, ghostExtId: Long, migratedGames: List<Str>)
+onCardUnlinked(luid: Str)
+onDataUpdated(extId: Long, game: Str, removeOldData: Bool)
}
class CardCreatedEvent {
+luid: Str
+extId: Long
}
class CardLinkedEvent {
+luid: Str
+oldExtId: Long?
+ghostExtId: Long
+migratedGames: List<Str>
}
class CardUnlinkedEvent {
+luid: Str
}
class DataUpdatedEvent {
+extId: Long
+isGhostCard: Bool
+game: Str
+removeOldData: Bool
}
class FedyEvent {
+cardCreated: CardCreatedEvent?
+cardLinked: CardLinkedEvent?
+cardUnlinked: CardUnlinkedEvent?
+dataUpdated: DataUpdatedEvent?
}
FedyEvent --> CardCreatedEvent
FedyEvent --> CardLinkedEvent
FedyEvent --> CardUnlinkedEvent
FedyEvent --> DataUpdatedEvent
Fedy --> FedyEvent
文件级别变更
提示和命令与 Sourcery 互动
自定义您的体验访问您的 仪表盘 以:
获取帮助Original review guide in EnglishReviewer's GuideOverhauls Fedy integration to use card-based data management by introducing dedicated data synchronization and card lifecycle endpoints, refactoring the internal event model and notification flow, updating controllers and game handlers to emit card events, enhancing migration/orphaning logic with explicit game context, and removing the legacy user-linking entity. ER diagram for removal of AquaNetUserFedy entity and tableerDiagram
AQUA_NET_USER_FEDY {
au_id BIGINT PK
created_at TIMESTAMP
}
AQUA_NET_USER_FEDY ||--|| AQUA_NET_USER : "au_id references au_id"
%% Table AQUA_NET_USER_FEDY is removed
Class diagram for new Fedy event model and endpointsclassDiagram
class Fedy {
+handleDataPull(key: Str, req: DataPullReq): DataPullRes
+handleDataPush(key: Str, req: DataPushReq): Any
+handleCardResolve(key: Str, req: CardResolveReq): CardResolveRes
+handleCardLink(key: Str, req: CardLinkReq): Any
+handleCardUnlink(key: Str, req: CardUnlinkReq): Any
+onCardCreated(luid: Str, extId: Long)
+onCardLinked(luid: Str, oldExtId: Long?, ghostExtId: Long, migratedGames: List<Str>)
+onCardUnlinked(luid: Str)
+onDataUpdated(extId: Long, game: Str, removeOldData: Bool)
}
class CardCreatedEvent {
+luid: Str
+extId: Long
}
class CardLinkedEvent {
+luid: Str
+oldExtId: Long?
+ghostExtId: Long
+migratedGames: List<Str>
}
class CardUnlinkedEvent {
+luid: Str
}
class DataUpdatedEvent {
+extId: Long
+isGhostCard: Bool
+game: Str
+removeOldData: Bool
}
class FedyEvent {
+cardCreated: CardCreatedEvent?
+cardLinked: CardLinkedEvent?
+cardUnlinked: CardUnlinkedEvent?
+dataUpdated: DataUpdatedEvent?
}
FedyEvent --> CardCreatedEvent
FedyEvent --> CardLinkedEvent
FedyEvent --> CardUnlinkedEvent
FedyEvent --> DataUpdatedEvent
Fedy --> FedyEvent
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
@sourcery-ai review |
|
@sourcery-ai summary |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
你好 - 我已审阅你的更改 - 以下是一些反馈:
- maybeNotifyAsync 中的跳过逻辑使用了
if (!props.enabled && !suppressEvents.get()),但当事件被抑制时也应该跳过,所以考虑更改为if (!props.enabled || suppressEvents.get())。 - handleCardLink 和 handleCardUnlink 目前返回 Unit—请添加一个明确的 SUCCESS(或适当的响应),以便客户端接收到预期的有效负载。
- 确认新的远程通知端点
/notify及其有效负载格式与 Fedy 服务器 API 对齐,因为它不再使用/notify/{event}路径。
AI 代理的提示
请处理此代码审查中的评论:
## 总体评论
- maybeNotifyAsync 中的跳过逻辑使用了 `if (!props.enabled && !suppressEvents.get())`,但当事件被抑制时也应该跳过,所以考虑更改为 `if (!props.enabled || suppressEvents.get())`。
- handleCardLink 和 handleCardUnlink 目前返回 Unit—请添加一个明确的 SUCCESS(或适当的响应),以便客户端接收到预期的有效负载。
- 确认新的远程通知端点 `/notify` 及其有效负载格式与 Fedy 服务器 API 对齐,因为它不再使用 `/notify/{event}` 路径。
## 单独评论
### 评论 1
<location> `src/main/java/icu/samnyan/aqua/net/Fedy.kt:248-250` </location>
<code_context>
}
+ // Apparently existing cards could possibly be fresh and never used in any game. Treat them as new cards.
+ private fun isCardFresh(c: Card): Bool {
+ fun <T : IUserData> checkForGame(repo: GenericUserDataRepo<T>, card: Card): Bool = repo.findByCard(card) == null
+ return when {
+ checkForGame(mai2UserDataRepo, c) -> false
+ checkForGame(chu3UserDataRepo, c) -> false
</code_context>
<issue_to_address>
**issue (bug_risk):** isCardFresh 中的逻辑似乎颠倒了;总是返回 false。
目前,如果任何 checkForGame 调用为 true,则 isCardFresh 返回 false,这意味着如果卡片缺少任何游戏的数据,则该卡片不是新的。预期的逻辑应该仅在所有 checkForGame 调用都为 true 时才返回 true,表示该卡片对于所有游戏都是新的。
</issue_to_address>
### 评论 2
<location> `src/main/java/icu/samnyan/aqua/net/Fedy.kt:220-225` </location>
<code_context>
+ }
+ }.let {}
+
+ private fun notify(event: FedyEvent) {
val MAX_RETRY = 3
- val body = body?.toJson() ?: "{}"
+ val body = event.toJson() ?: "{}"
var retry = 0
var shouldRetry = true
- while (retry < MAX_RETRY) {
+ while (true) {
try {
- val response = "${props.remote.trimEnd('/')}/notify/${event.name}".request()
</code_context>
<issue_to_address>
**issue (bug_risk):** notify 中的无限重试循环可能导致资源耗尽。
限制重试可以防止端点持续不可用时可能发生的资源耗尽。请恢复 MAX_RETRY 限制以避免无限循环。
</issue_to_address>
### 评论 3
<location> `src/main/java/icu/samnyan/aqua/net/Fedy.kt:209-211` </location>
<code_context>
+ })
+
+ private fun maybeNotifyAsync(event: FedyEvent) = maybeNotifyAsync({ event })
+ private fun maybeNotifyAsync(getEvent: () -> FedyEvent?) = if (!props.enabled && !suppressEvents.get()) {} else CompletableFuture.runAsync {
+ var event: FedyEvent? = null
+ try {
</code_context>
<issue_to_address>
**suggestion (bug_risk):** 事件通知的条件可能不正确。
当前条件仅在“enabled”为 false 且事件未被抑制时才跳过通知。要在禁用或抑制任一情况时跳过通知,请使用 'if (!props.enabled || suppressEvents.get())'。
```suggestion
private fun maybeNotifyAsync(getEvent: () -> FedyEvent?) = if (!props.enabled || suppressEvents.get()) {} else CompletableFuture.runAsync {
var event: FedyEvent? = null
try {
```
</issue_to_address>
### 4号评论
<location> `src/main/java/icu/samnyan/aqua/net/CardController.kt:227-229` </location>
<code_context>
-// it.pdId = card.aquaUser!!.ghostCard
-// }
- }
+ val dataRepo = dataRepos[game] ?: return@forEach
+ migrateCard(game, dataRepo, cardRepo, crd)
+ remainingGames.remove(game)
</code_context>
<issue_to_address>
**suggestion (bug_risk):** 未知游戏的静默返回可能隐藏迁移问题。
考虑在 dataRepos 中找不到游戏时添加一条日志消息,以帮助调试迁移问题。
```suggestion
val dataRepo = dataRepos[game]
if (dataRepo == null) {
logger.warn("Migration issue: dataRepo not found for game '$game'. Card id: ${crd.id}")
return@forEach
}
migrateCard(game, dataRepo, cardRepo, crd)
remainingGames.remove(game)
```
</issue_to_address>
### 评论 5
<location> `src/main/java/icu/samnyan/aqua/net/Fedy.kt:73` </location>
<code_context>
- val userFedy = userFedyRepo.findByAquaNetUserAuId(auId) ?: 404 - "User not linked"
- val user = userRepo.findByAuId(auId) ?: 404 - "User not found"
- return user
+ val suppressEvents = ThreadLocal.withInitial { false }
+ private fun <T> handleFedy(key: Str, block: () -> T): T {
+ val old = suppressEvents.get()
</code_context>
<issue_to_address>
**suggestion (bug_risk):** ThreadLocal 在异步上下文中可能不安全。
ThreadLocal 可能无法在协程或线程池上下文切换中可靠地维护状态。如果可能进行异步执行,请考虑使用替代方案进行上下文传播。
建议的实现:
```
private fun <T> handleFedy(key: Str, suppressEvents: Boolean = false, block: (Boolean) -> T): T {
key.checkKey()
return block(true)
}
```
- `handleFedy` 的所有用法都必须更新以接受新的签名:如果需要,传递 `suppressEvents` 标志,并更新 lambda 以接受该标志作为参数。
- 如果需要将 `suppressEvents` 进一步传播到调用堆栈中,请将其显式作为函数参数传递。
- 如果正在使用协程并希望传播上下文,请考虑使用 `CoroutineContext` 和 `ThreadLocal.asContextElement()` 来处理更高级的场景。
</issue_to_address>帮助我更有用!请点击每个评论上的 👍 或 👎,我将使用反馈来改进你的评论。
Original comment in English
Hey there - I've reviewed your changes - here's some feedback:
- The skip logic in maybeNotifyAsync uses
if (!props.enabled && !suppressEvents.get())but it should also skip when events are suppressed, so consider changing toif (!props.enabled || suppressEvents.get()). - handleCardLink and handleCardUnlink currently return Unit—add an explicit SUCCESS (or appropriate response) so clients receive the expected payload.
- Confirm that the new remote notify endpoint
/notifyand its payload format align with the Fedy server API, since it no longer uses/notify/{event}paths.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The skip logic in maybeNotifyAsync uses `if (!props.enabled && !suppressEvents.get())` but it should also skip when events are suppressed, so consider changing to `if (!props.enabled || suppressEvents.get())`.
- handleCardLink and handleCardUnlink currently return Unit—add an explicit SUCCESS (or appropriate response) so clients receive the expected payload.
- Confirm that the new remote notify endpoint `/notify` and its payload format align with the Fedy server API, since it no longer uses `/notify/{event}` paths.
## Individual Comments
### Comment 1
<location> `src/main/java/icu/samnyan/aqua/net/Fedy.kt:248-250` </location>
<code_context>
}
+ // Apparently existing cards could possibly be fresh and never used in any game. Treat them as new cards.
+ private fun isCardFresh(c: Card): Bool {
+ fun <T : IUserData> checkForGame(repo: GenericUserDataRepo<T>, card: Card): Bool = repo.findByCard(card) == null
+ return when {
+ checkForGame(mai2UserDataRepo, c) -> false
+ checkForGame(chu3UserDataRepo, c) -> false
</code_context>
<issue_to_address>
**issue (bug_risk):** Logic in isCardFresh appears inverted; always returns false.
Currently, isCardFresh returns false if any checkForGame call is true, which means the card is not fresh if it is missing data for any game. The intended logic should return true only if all checkForGame calls are true, indicating the card is fresh for all games.
</issue_to_address>
### Comment 2
<location> `src/main/java/icu/samnyan/aqua/net/Fedy.kt:220-225` </location>
<code_context>
+ }
+ }.let {}
+
+ private fun notify(event: FedyEvent) {
val MAX_RETRY = 3
- val body = body?.toJson() ?: "{}"
+ val body = event.toJson() ?: "{}"
var retry = 0
var shouldRetry = true
- while (retry < MAX_RETRY) {
+ while (true) {
try {
- val response = "${props.remote.trimEnd('/')}/notify/${event.name}".request()
</code_context>
<issue_to_address>
**issue (bug_risk):** Infinite retry loop in notify may cause resource exhaustion.
Limiting retries prevents potential resource exhaustion if the endpoint remains unavailable. Please restore the MAX_RETRY limit to avoid infinite loops.
</issue_to_address>
### Comment 3
<location> `src/main/java/icu/samnyan/aqua/net/Fedy.kt:209-211` </location>
<code_context>
+ })
+
+ private fun maybeNotifyAsync(event: FedyEvent) = maybeNotifyAsync({ event })
+ private fun maybeNotifyAsync(getEvent: () -> FedyEvent?) = if (!props.enabled && !suppressEvents.get()) {} else CompletableFuture.runAsync {
+ var event: FedyEvent? = null
+ try {
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Condition for event notification may be incorrect.
The current condition only skips notification when both 'enabled' is false and events are not suppressed. To skip notification when either is disabled or suppressed, use 'if (!props.enabled || suppressEvents.get())'.
```suggestion
private fun maybeNotifyAsync(getEvent: () -> FedyEvent?) = if (!props.enabled || suppressEvents.get()) {} else CompletableFuture.runAsync {
var event: FedyEvent? = null
try {
```
</issue_to_address>
### Comment 4
<location> `src/main/java/icu/samnyan/aqua/net/CardController.kt:227-229` </location>
<code_context>
-// it.pdId = card.aquaUser!!.ghostCard
-// }
- }
+ val dataRepo = dataRepos[game] ?: return@forEach
+ migrateCard(game, dataRepo, cardRepo, crd)
+ remainingGames.remove(game)
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Silent return for unknown game may hide migration issues.
Consider adding a log message when a game is not found in dataRepos to aid in debugging migration issues.
```suggestion
val dataRepo = dataRepos[game]
if (dataRepo == null) {
logger.warn("Migration issue: dataRepo not found for game '$game'. Card id: ${crd.id}")
return@forEach
}
migrateCard(game, dataRepo, cardRepo, crd)
remainingGames.remove(game)
```
</issue_to_address>
### Comment 5
<location> `src/main/java/icu/samnyan/aqua/net/Fedy.kt:73` </location>
<code_context>
- val userFedy = userFedyRepo.findByAquaNetUserAuId(auId) ?: 404 - "User not linked"
- val user = userRepo.findByAuId(auId) ?: 404 - "User not found"
- return user
+ val suppressEvents = ThreadLocal.withInitial { false }
+ private fun <T> handleFedy(key: Str, block: () -> T): T {
+ val old = suppressEvents.get()
</code_context>
<issue_to_address>
**suggestion (bug_risk):** ThreadLocal usage may not be safe in async contexts.
ThreadLocal may not reliably maintain state across coroutine or thread pool context switches. If async execution is possible, consider alternatives for context propagation.
Suggested implementation:
```
private fun <T> handleFedy(key: Str, suppressEvents: Boolean = false, block: (Boolean) -> T): T {
key.checkKey()
return block(true)
}
```
- All usages of `handleFedy` must be updated to accept the new signature: pass the `suppressEvents` flag if needed, and update the lambda to accept the flag as a parameter.
- If you need to propagate `suppressEvents` further down the call stack, pass it explicitly as a function argument.
- If you are using coroutines and want to propagate context, consider using `CoroutineContext` and `ThreadLocal.asContextElement()` for more advanced scenarios.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| private fun isCardFresh(c: Card): Bool { | ||
| fun <T : IUserData> checkForGame(repo: GenericUserDataRepo<T>, card: Card): Bool = repo.findByCard(card) == null | ||
| return when { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (bug_risk): isCardFresh 中的逻辑似乎颠倒了;总是返回 false。
目前,如果任何 checkForGame 调用为 true,则 isCardFresh 返回 false,这意味着如果卡片缺少任何游戏的数据,则该卡片不是新的。预期的逻辑应该仅在所有 checkForGame 调用都为 true 时才返回 true,表示该卡片对于所有游戏都是新的。
Original comment in English
issue (bug_risk): Logic in isCardFresh appears inverted; always returns false.
Currently, isCardFresh returns false if any checkForGame call is true, which means the card is not fresh if it is missing data for any game. The intended logic should return true only if all checkForGame calls are true, indicating the card is fresh for all games.
| private fun maybeNotifyAsync(getEvent: () -> FedyEvent?) = if (!props.enabled && !suppressEvents.get()) {} else CompletableFuture.runAsync { | ||
| var event: FedyEvent? = null | ||
| try { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (bug_risk): 事件通知的条件可能不正确。
当前条件仅在“enabled”为 false 且事件未被抑制时才跳过通知。要在禁用或抑制任一情况时跳过通知,请使用 'if (!props.enabled || suppressEvents.get())'。
| private fun maybeNotifyAsync(getEvent: () -> FedyEvent?) = if (!props.enabled && !suppressEvents.get()) {} else CompletableFuture.runAsync { | |
| var event: FedyEvent? = null | |
| try { | |
| private fun maybeNotifyAsync(getEvent: () -> FedyEvent?) = if (!props.enabled || suppressEvents.get()) {} else CompletableFuture.runAsync { | |
| var event: FedyEvent? = null | |
| try { |
Original comment in English
suggestion (bug_risk): Condition for event notification may be incorrect.
The current condition only skips notification when both 'enabled' is false and events are not suppressed. To skip notification when either is disabled or suppressed, use 'if (!props.enabled || suppressEvents.get())'.
| private fun maybeNotifyAsync(getEvent: () -> FedyEvent?) = if (!props.enabled && !suppressEvents.get()) {} else CompletableFuture.runAsync { | |
| var event: FedyEvent? = null | |
| try { | |
| private fun maybeNotifyAsync(getEvent: () -> FedyEvent?) = if (!props.enabled || suppressEvents.get()) {} else CompletableFuture.runAsync { | |
| var event: FedyEvent? = null | |
| try { |
| val dataRepo = dataRepos[game] ?: return@forEach | ||
| migrateCard(game, dataRepo, cardRepo, crd) | ||
| remainingGames.remove(game) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (bug_risk): 未知游戏的静默返回可能隐藏迁移问题。
考虑在 dataRepos 中找不到游戏时添加一条日志消息,以帮助调试迁移问题。
| val dataRepo = dataRepos[game] ?: return@forEach | |
| migrateCard(game, dataRepo, cardRepo, crd) | |
| remainingGames.remove(game) | |
| val dataRepo = dataRepos[game] | |
| if (dataRepo == null) { | |
| logger.warn("Migration issue: dataRepo not found for game '$game'. Card id: ${crd.id}") | |
| return@forEach | |
| } | |
| migrateCard(game, dataRepo, cardRepo, crd) | |
| remainingGames.remove(game) |
Original comment in English
suggestion (bug_risk): Silent return for unknown game may hide migration issues.
Consider adding a log message when a game is not found in dataRepos to aid in debugging migration issues.
| val dataRepo = dataRepos[game] ?: return@forEach | |
| migrateCard(game, dataRepo, cardRepo, crd) | |
| remainingGames.remove(game) | |
| val dataRepo = dataRepos[game] | |
| if (dataRepo == null) { | |
| logger.warn("Migration issue: dataRepo not found for game '$game'. Card id: ${crd.id}") | |
| return@forEach | |
| } | |
| migrateCard(game, dataRepo, cardRepo, crd) | |
| remainingGames.remove(game) |
| val userFedy = userFedyRepo.findByAquaNetUserAuId(auId) ?: 404 - "User not linked" | ||
| val user = userRepo.findByAuId(auId) ?: 404 - "User not found" | ||
| return user | ||
| val suppressEvents = ThreadLocal.withInitial { false } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (bug_risk): ThreadLocal 在异步上下文中可能不安全。
ThreadLocal 可能无法在协程或线程池上下文切换中可靠地维护状态。如果可能进行异步执行,请考虑使用替代方案进行上下文传播。
建议的实现:
private fun <T> handleFedy(key: Str, suppressEvents: Boolean = false, block: (Boolean) -> T): T {
key.checkKey()
return block(true)
}
handleFedy的所有用法都必须更新以接受新的签名:如果需要,传递suppressEvents标志,并更新 lambda 以接受该标志作为参数。- 如果需要将
suppressEvents进一步传播到调用堆栈中,请将其显式作为函数参数传递。 - 如果正在使用协程并希望传播上下文,请考虑使用
CoroutineContext和ThreadLocal.asContextElement()来处理更高级的场景。
Original comment in English
suggestion (bug_risk): ThreadLocal usage may not be safe in async contexts.
ThreadLocal may not reliably maintain state across coroutine or thread pool context switches. If async execution is possible, consider alternatives for context propagation.
Suggested implementation:
private fun <T> handleFedy(key: Str, suppressEvents: Boolean = false, block: (Boolean) -> T): T {
key.checkKey()
return block(true)
}
- All usages of
handleFedymust be updated to accept the new signature: pass thesuppressEventsflag if needed, and update the lambda to accept the flag as a parameter. - If you need to propagate
suppressEventsfurther down the call stack, pass it explicitly as a function argument. - If you are using coroutines and want to propagate context, consider using
CoroutineContextandThreadLocal.asContextElement()for more advanced scenarios.
Sourcery 总结
重构 Fedy 集成,使其使用基于卡的數據管理和事件通知,引入新的數據拉取/推送和卡片管理端點,並移除舊的用戶鏈接實現。
新功能:
/data/pull和/data/push端點,用於通過卡片 ID 進行外部遊戲數據同步/card/resolve、/card/link和/card/unlink存根端點,用於卡片生命週期管理改進:
CardRepository和外部卡片 ID,而不是AquaNetUser鏈接CardController、Frontier、AimeDB和遊戲導入控制器中,於卡片創建、鏈接、解除鏈接和數據更新時觸發 Fedy 事件DataUpdated事件日常任務:
AquaNetUserFedy實體並刪除其數據庫表Original summary in English
Sourcery 总结
全面改进 Fedy 集成以使用基于卡的數據管理:用卡片解析/链接/取消链接和数据拉取/推送端点替换旧的用户链接,发送卡片生命周期和数据更新事件,相应地更新控制器和游戏处理器,并移除旧的 AquaNetUserFedy 实体
新功能:
/data/pull和/data/push端点,用于通过卡片extId同步游戏数据/card/resolve、/card/link和/card/unlink端点,用于管理卡片生命周期CardCreated、CardLinked、CardUnlinked、DataUpdated),以通知下游服务改进:
CardRepository和外部卡片 ID,而不是AquaNetUser链接DataUpdated事件CardController、Frontier和AimeDB中,在卡片创建、链接和取消链接时发送 Fedy 事件CardGameService,以在幽灵卡和活跃卡之间迁移和孤立游戏数据杂项:
AquaNetUserFedy实体并删除aqua_net_user_fedy表Original summary in English
Sourcery 总结
重构 Fedy 集成,采用基于卡的外部 ID 数据管理方式,将基于用户的链接替换为卡生命周期操作,并为控制器和服务间的卡创建、链接、解除链接和数据更新发出结构化事件。
新功能:
/data/pull和/data/push端点,用于通过卡外部 ID 同步游戏数据/card/resolve、/card/link和/card/unlink端点,用于管理卡生命周期改进:
CardRepository和外部卡 ID,而非AquaNetUserFedy链接CardGameService,以支持按游戏上下文迁移和孤立游戏数据杂项:
AquaNetUserFedy实体并删除其数据库表Original summary in English
Summary by Sourcery
Refactor Fedy integration to adopt card-based data management with external IDs, replace user-based linking with card lifecycle operations, and emit structured events for card creation, linking, unlinking, and data updates across controllers and services.
New Features:
Enhancements:
Chores: